#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/wait.h>

#define SHM_SIZE 2048
#define LOOPS 3

// 信号量操作联合体
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

// P操作
void sem_p(int sem_id, int sem_num) {
    struct sembuf sem_b;
    sem_b.sem_num = sem_num;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;
    semop(sem_id, &sem_b, 1);
}

// V操作
void sem_v(int sem_id, int sem_num) {
    struct sembuf sem_b;
    sem_b.sem_num = sem_num;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    semop(sem_id, &sem_b, 1);
}

int main() {
    int shmid;
    pid_t pid;
    char *shm_addr;
    int sem_id;

    // 1. 创建共享内存
    if ((shmid = shmget(IPC_PRIVATE, SHM_SIZE, 0666|IPC_CREAT)) == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }

    // 2. 创建信号量集（包含两个信号量）
    if ((sem_id = semget(IPC_PRIVATE, 2, 0666|IPC_CREAT)) == -1) {
        perror("semget failed");
        exit(EXIT_FAILURE);
    }

    // 初始化信号量
    union semun init;
    unsigned short values[2] = {1, 0}; // sem0=1（写信号量），sem1=0（读信号量）
    init.array = values;
    if (semctl(sem_id, 0, SETALL, init) == -1) {
        perror("semctl init failed");
        exit(EXIT_FAILURE);
    }

    // 3. 创建子进程
    pid = fork();
    if (pid < 0) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) { // 子进程（读取数据）
        shm_addr = (char*)shmat(shmid, NULL, 0);
        if (shm_addr == (char*)-1) {
            perror("shmat failed");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < LOOPS; i++) {
            sem_p(sem_id, 1); // 等待读信号量
            printf("Child received: %s\n", shm_addr);
            sem_v(sem_id, 0); // 释放写信号量
        }

        shmdt(shm_addr);
        exit(EXIT_SUCCESS);
    } 
    else { // 父进程（写入数据）
        shm_addr = (char*)shmat(shmid, NULL, 0);
        if (shm_addr == (char*)-1) {
            perror("shmat failed");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < LOOPS; i++) {
            sem_p(sem_id, 0); // 等待写信号量
            snprintf(shm_addr, SHM_SIZE, "Parent Message %d", i+1);
            printf("Parent wrote: %s\n", shm_addr);
            sem_v(sem_id, 1); // 释放读信号量
        }

        // 等待子进程结束
        wait(NULL);
        
        // 清理资源
        shmdt(shm_addr);
        shmctl(shmid, IPC_RMID, NULL);
        semctl(sem_id, 0, IPC_RMID);
    }

    return 0;
}
